史vm题
ida打开main函数看到进入就是vm虚拟机

点进去发现是一个巨大的switch case循环,结构非常明显,有四个32位寄存器,其他和题目关系不大
二十多个case全部写handle去解析opcode的话难度很大,而且还要指令长度之类的问题,可以采用在指令入口点下条件断点的方式,然后动态执行整个vm,在执行指令的同时输出执行的操作,这样就可以获取vm指令的伪代码了



然后跑一遍就能拿到执行的指令了

可以看到非常长,这里我同时在指令前输出了执行前寄存器的值,同时这里的下标也是处理过的,如果直接输出寄存器的值下标会非常大,用了一个python脚本对下标做了偏移让伪代码好看点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import re import sys
def process_text(text): pattern1 = r'heap\[(938\d+)\]' matches1 = re.findall(pattern1, text)
pattern2 = r'heap\[(1844\d+)\]' matches2 = re.findall(pattern2, text)
if matches1: indices1 = [int(idx) for idx in matches1] base1 = min(indices1)
for idx in sorted(set(indices1), reverse=True): offset = idx - base1 text = text.replace(f'heap[{idx}]', f'heap_1[{offset}]')
if matches2: indices2 = [int(idx) for idx in matches2] base2 = min(indices2)
for idx in sorted(set(indices2), reverse=True): offset = idx - base2 text = text.replace(f'heap[{idx}]', f'heap_2[{offset}]')
return text
def main(): if len(sys.argv) > 1: input_file = sys.argv[1] with open(input_file, 'r') as f: text = f.read() else: text = sys.stdin.read()
processed_text = process_text(text)
print(processed_text)
if __name__ == "__main__": main()
|
然后看cmp xx 48可以看出来明显有一个循环,结合各种左右移操作,猜测是tea系列的加密,于是截取一段循环抄算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| reg3 = heap_2[56]; reg3 >>= 6; reg0 = heap_2[56]; reg0 <<= 5; reg3 ^= reg0; reg3 += heap_2[56]; reg2 = heap_2[64]; reg2 &= 3; reg0 = heap_2[68]; reg2 = heap_1[0]; reg2 += heap_2[64]; reg3 ^= reg2; reg3 += heap_2[60]; heap_2[60] = 1352750335; reg3 = heap_2[64]; reg3 -= heap_2[32]; heap_2[64] = 2189458126; reg0 = heap_2[60]; reg0 >>= 7; reg2 = heap_2[60]; reg2 <<= 3; reg0 ^= reg2; reg0 += heap_2[60]; reg3 = heap_2[64]; reg3 >>= 11; reg3 &= 3; reg2 = heap_2[68]; reg3 = heap_1[12]; reg3 += heap_2[64]; reg0 ^= reg3; reg0 += heap_2[56]; heap_2[56] = 3924216427; reg2 = heap_2[44]; reg2 += 1; heap_2[44] = 5;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void enc(unsigned int v0, unsigned int v1, unsigned int *key) { unsigned int sum = 0; unsigned int tmp_reg3 = 0, tmp_reg2 = 0, tmp_reg0 = 0; for (int i = 0; i < 48; i++) { v0 += (((v1 >> 6) ^ (v1 << 5) )+ v1) ^ (key[sum & 3] + sum); sum -= 421101834; v1 += (((v0 >> 7) ^ (v0 << 3)) + v0) ^ (key[(sum >> 11) & 3] + sum); } }
|
key 的来源是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| heap_2[0] = 67;
heap_2[1] = 104;
heap_2[2] = 111;
heap_2[3] = 118;
heap_2[4] = 121;
heap_2[5] = 95;
heap_2[6] = 105;
heap_2[7] = 110;
heap_2[8] = 107;
heap_2[9] = 101;
heap_2[10] = 121;
heap_2[11] = 95;
heap_2[12] = 119;
heap_2[13] = 51;
heap_2[14] = 54;
heap_2[15] = 95;
|
其中可能在整理heap时脚本出了点问题,储存key的被错误命名成了heap2,但是还是能辨别出key
于是写解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| void dec(unsigned int *v, unsigned int *key) { unsigned int v0 = v[0]; unsigned int v1 = v[1]; unsigned int delta = 421101834; unsigned int sum = 0; for (int i = 0; i < 48; i++) sum -= delta; for (int i = 0; i < 48; i++) { v1 -= (((v0 >> 7) ^ (v0 << 3)) + v0) ^ (key[(sum >> 11) & 3] + sum); sum += delta; v0 -= (((v1 >> 6) ^ (v1 << 5)) + v1) ^ (key[sum & 3] + sum); } for (int i = 0; i < 4; i++) printf("%c", ((unsigned char *)&v0)[i]); for (int i = 0; i < 4; i++) printf("%c", ((unsigned char *)&v1)[i]); } int main() { unsigned char key[] = "Chovy_inkey_w36_"; unsigned char cipher[] = "\xE5\xDF\xF0\xA1\xF4\xBD\x6A\xDB\x1B\xE9\xDD\x20\r\x9D!YгY)\xB9\xEC\x2F\xC0\"~\xAD\xE1\xB0\x15\xB6)"; for (int i = 0; i < 32; i += 8) dec((unsigned int *)&cipher[i], (unsigned int *)key); return 0; }
|
flag{D0_yOu_l1k3_VmmmmMMMMMmmm?}